/*
 * AbstractResource.java
 */

package xq.util.resource;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

import static xq.util.CommonUtil.choose;
import static xq.util.IOUtil.getLocalFile;

/**
 *
 * @param <T> The resource class type
 * @author ThorntonRP
 */
public abstract class AbstractResource<T> implements Resource, Cloneable {

	private final File resourceFile;
	private final URL resourceURL;
	private final URI resourceURI;
	private final String contentType;

	public AbstractResource(String resourceURI, String contentType) throws URISyntaxException {
		if (resourceURI == null) {
			throw new IllegalArgumentException("resourceURI cannot be null");
		}
		this.resourceURL = null;
		this.resourceFile = null;
		this.contentType = choose(contentType, "unknown");
		this.resourceURI = new URI(resourceURI);
	}
	
	public AbstractResource(URI resourceURI, String contentType) {
		if (resourceURI == null) {
			throw new IllegalArgumentException("resourceURI cannot be null");
		}
		this.resourceURI = resourceURI;
		this.resourceURL = null;
		this.resourceFile = null;
		this.contentType = choose(contentType, "unknown");
	}

	public AbstractResource(File resourceFile, String contentType) throws IOException {
		if (resourceFile == null) {
			throw new IllegalArgumentException("resourceFile cannot be null");
		}
		this.resourceFile = resourceFile;
		this.resourceURI = resourceFile.toURI();
		this.resourceURL = resourceURI.toURL();
		this.contentType = choose(contentType, choose(resourceURL.openConnection().getContentType(), "unknown"));
	}

	public AbstractResource(URL resourceURL, String contentType) throws IOException {
		if (resourceURL == null) {
			throw new IllegalArgumentException("resourceURL cannot be null");
		}
		this.resourceURL = resourceURL;
		this.resourceFile = getLocalFile(resourceURL);
		this.contentType = choose(contentType, choose(resourceURL.openConnection().getContentType(), "unknown"));
		try {
			this.resourceURI = new URI(resourceURL.toString().replace(" ", "%20"));
		} catch (URISyntaxException ex) {
			throw new ResourceException("Failed to initialize resource.", ex);
		}
	}

	public File getResourceFile() {
		return resourceFile;
	}

	public URL getResourceURL() {
		return resourceURL;
	}

	public URI getResourceURI() {
		return resourceURI;
	}

	public String getContentType() {
		return contentType;
	}

	public abstract T getContent() throws IOException;
	
	@Override
	public int hashCode() {
		int hash = 3;
		hash = 97 * hash + this.resourceURI.hashCode();
		return hash;
	}

	/**
	 * This method returns true only if the following conditions are all met:
	 * <ul>
	 *   <li>The specified object is non-null</li>
	 *   <li>The specified object is of the exact same type as this object.</li>
	 *   <li>The specified object has a URL that is equivalent to this object's URL.</li>
	 * </ul>
	 * @param o The object to be compared.
	 * @return true if the given object is equal to this object; false otherwise.
	 */
	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		} else if (o == null) {
			return false;
		} else if (getClass() != o.getClass()) {
			return false;
		} else {
			AbstractResource that = (AbstractResource) o;
			return this.resourceURI.equals(that.resourceURI);
		}
	}

	@Override
	public Object clone() {
		try {
			return super.clone();
		} catch (CloneNotSupportedException ex) {
			throw new IllegalStateException(ex);
		}
	}

	@Override
	public String toString() {
		return getClass() + "[" + resourceURI.toString() + "]";
	}
}
